﻿#region Copyright
// Copyright (c) 2011-2012 RaisingForce Team (Alikin Sergey)
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using RaisingForce.Raise.Presentation.Localization;
using RaisingForce.Raise.Presentation.ViewModel;

namespace RaisingForce.RaiseHardwareTest.ViewModel
{
    sealed internal class LanguageSelectorViewModel :
        ViewModelBase,
        IEnumerable<LanguageViewModel>,
        INotifyCollectionChanged
    {
        #region Constructors

        public LanguageSelectorViewModel()
            : base()
        {
            this.languages.CollectionChanged +=
                this.languages_CollectionChanged;
        }

        #endregion

        #region Private Declarations

        private readonly ObservableCollection<LanguageViewModel> languages =
            new ObservableCollection<LanguageViewModel>();

        private void languages_CollectionChanged(
            object sender,
            NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems == null
                || e.NewItems.Count != e.OldItems.Count)
            {
                this.OnPropertyChanged(propLanguageCount);
            }
            this.OnCollectionChanged(e);
        }

        private void registerHandlers(LanguageViewModel languageViewModel)
        {
            Contract.Requires(languageViewModel != null);
            languageViewModel.Selected += this.languageViewModel_Selected;
        }

        private void unregisterHandlers(LanguageViewModel languageViewModel)
        {
            Contract.Requires(languageViewModel != null);
            languageViewModel.Selected -= this.languageViewModel_Selected;
        }

        private void languageViewModel_Selected(
            object sender,
            LocalizationLanguageEventArgs e)
        {
            this.SelectedLanguage = e.Language;
        }

        [ContractInvariantMethod]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Required for code contracts.")]
        private void ObjectInvariant()
        {
            Contract.Invariant(this.selectedLanguage != null || this.LanguageCount == 0);
        }

        #endregion

        #region ViewModel Properties

        private const string propLanguageCount = "LanguageCount";

        public int LanguageCount
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);
                return this.languages.Count;
            }
        }

        private LocalizationLanguage selectedLanguage;

        public LocalizationLanguage SelectedLanguage
        {
            get
            {
                Contract.Ensures(Contract.Result<LocalizationLanguage>() != null || this.LanguageCount == 0);
                return this.selectedLanguage;
            }
            private set
            {
                if (!object.ReferenceEquals(this.selectedLanguage, value))
                {
                    this.selectedLanguage = value;
                    this.OnSelectedLanguageChanged();
                }
            }
        }

        #endregion

        #region Public Collection Methods

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Ликвидировать объекты перед потерей области")]
        public void AddLanguage(LocalizationLanguage language)
        {
            Contract.Requires(language != null);
            Contract.Ensures(this.LanguageCount == Contract.OldValue(this.LanguageCount) + 1);
            LanguageViewModel languageViewModel =
                new LanguageViewModel(language, this.languages);
            this.registerHandlers(languageViewModel);
            this.languages.Add(languageViewModel);
            // If element is first in collection, then mark it as selected;
            // other way it is not selected (by default).
            if (this.languages.Count == 1)
            {
                languageViewModel.IsSelected = true;
            }
        }

        public void AddMultipleLanguages(
            IEnumerable<LocalizationLanguage> languages)
        {
            Contract.Requires(languages != null);
            foreach (LocalizationLanguage language in languages)
            {
                Contract.Assume(language != null);
                this.AddLanguage(language);
            }
        }

        public void RemoveAllLanguages()
        {
            Contract.Ensures(this.LanguageCount == 0);
            if (this.languages.Count > 0)
            {
                foreach (LanguageViewModel languageViewModel in this.languages)
                {
                    Contract.Assume(languageViewModel != null);
                    this.unregisterHandlers(languageViewModel);
                }
                this.languages.Clear();
                this.SelectedLanguage = null;
            }
        }

        #endregion

        #region Public TrySelectLanguage Method

        public bool TrySelectLanguage(string languageCultureName)
        {
            Contract.Requires(languageCultureName != null);
            return this.trySelectLanguage(
                vm => vm.Language.CultureName == languageCultureName);
        }

        public bool TrySelectLanguage(CultureInfo languageCultureInfo)
        {
            Contract.Requires(languageCultureInfo != null);
            return this.trySelectLanguage(
                vm => languageCultureInfo.Equals(vm.Language.CultureInfo));
        }

        private bool trySelectLanguage(
            Func<LanguageViewModel, bool> predicate)
        {
            Contract.Requires(predicate != null);
            LanguageViewModel viewModel = this.languages
                .FirstOrDefault(predicate);
            if (viewModel != null)
            {
                viewModel.IsSelected = true;
                return true;
            }
            else
            {
                return false;
            }
        }

        #endregion

        #region CollectionChanged Event

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            Contract.Requires(e != null);
            NotifyCollectionChangedEventHandler handler = this.CollectionChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion

        #region SelectedLanguageChanged Event

        public event EventHandler SelectedLanguageChanged;

        private void OnSelectedLanguageChanged()
        {
            this.OnEvent(this.SelectedLanguageChanged);
        }

        #endregion

        #region IEnumerable<LanguageViewModel> Members

        public IEnumerator<LanguageViewModel> GetEnumerator()
        {
            return this.languages.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion
    }
}
